<?php
namespace Xxfaxy\DryBundle\Services;

class Crud
{

    protected $container;

    private $pdo = null;

    private $sql = null;

    private $cache = null;

    private $cacheTime = 0;

    private $cacheKey = '';

    public function __construct($service_container)
    {
        $this->container = $service_container;
        $this->pdo = $this->container->get('service.pdo')->get();
        $this->sql = $this->container->get('service.sql');
        if($this->container->getParameter('crud_cache_use')){
            $crudCacheService = $this->container->getParameter('crud_cache_service');
            $crudCachePrefix = $this->container->getParameter('crud_cache_prefix');
            $this->cache = $this->container->get($crudCacheService)->setPrefix($crudCachePrefix);
        }
    }

    public function setPdoAttribute($key, $value)
    {
        $this->pdo->setAttribute($key, $value);
        return $this;
    }

    public function switch2assoc()
    {
        return $this->setPdoAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_ASSOC);
    }

    public function switch2obj()
    {
        return $this->setPdoAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_OBJ);
    }

    public function switch2num()
    {
        return $this->setPdoAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_NUM);
    }

    public function getSql()
    {
        return $this->sql;
    }

    public function getCache()
    {
        return $this->cache;
    }

    public function cache($time, $key = '')
    {
        if($time == 0){
            $this->cacheTime = 0;
        }
        else if(is_int($time)){
            $this->cacheTime = $time;
        }
        else if($time == 'today'){
            $this->cacheTime = strtotime(date('Y-m-d 23:59:59'))-time();
        }
        else{
            $i = (int) $time;
            $unit = str_replace($i, '', $time);
            $second = array(
                'y' => 86400*365,
                'm' => 86400*30,
                'd' => 86400,
                'h' => 3600,
                'i' => 60,
                's' => 1
            );
            $this->cacheTime = $second[$unit]*$i;
        }
        if($key != ''){
            $this->cacheKey = 'fixed_'.$key;
        }
        return $this;
    }

    public function getCacheKeys($pattern = '*')
    {
        return $this->cache->keys($pattern);
    }

    private function makeCacheKey($data)
    {
        return 'else_'.md5(serialize($data));
    }

    private function getTheCacheKey($data)
    {
        if($this->cacheKey != ''){
            return $this->cacheKey;
        }
        else{
            return $this->makeCacheKey($data);
        }
    }

    private function getCacheData($data)
    {
        $result = array('status' => false, 'data' => '');
        $key = $this->getTheCacheKey($data);
        if($this->cache != null && $this->cacheTime != 0 && $this->cache->has($key)){
            $raw = $this->cache->get($key);
            if(time() < $raw['time']){
                $result['status'] = true;
                $result['data'] = $raw['data'];
                $this->cacheTime = 0;
                $this->cacheKey = '';
            }
            else{
                $this->cache->remove($key);
            }
        }
        return $result;
    }

    private function setCacheData($data, $save)
    {
        if($this->cache != null && $this->cacheTime > 0){
            $key = $this->getTheCacheKey($data);
            $raw = array(
                'time' => time() + $this->cacheTime,
                'data' => $save
            );
            $this->cache->set($key, $raw);
        }
    }

    public function fetch($data)
    {
        if(is_string($data)){
            $data = array(
                'sql' => $data,
                'placeholder' => array()
            );
        }
        $cache = $this->getCacheData($data);
        if($cache['status']){
            return $cache['data'];
        }
        $sm = $this->pdo->prepare($data['sql']);
        foreach($data['placeholder'] as $k => $v){
            $sm->bindValue($k, $v);
        }
        $sm->execute();
        $return = $sm->fetch();
        $this->setCacheData($data, $return);
        return $return;
    }

    public function fetchAll($data)
    {
        if(is_string($data)){
            $data = array(
                'sql' => $data,
                'placeholder' => array()
            );
        }
        $sm = $this->pdo->prepare($data['sql']);
        foreach($data['placeholder'] as $k => $v){
            $sm->bindValue($k, $v);
        }
        $sm->execute();
        return $sm->fetchAll();
    }

    public function fetchColumn($data)
    {
        if(is_string($data)){
            $data = array(
                'sql' => $data,
                'placeholder' => array()
            );
        }
        $sm = $this->pdo->prepare($data['sql']);
        foreach($data['placeholder'] as $k => $v){
            $sm->bindValue($k, $v);
        }
        $sm->execute();
        return $sm->fetchColumn();
    }

    public function runInsert($data)
    {
        if(is_string($data)){
            $data = array(
                'sql' => $data,
                'placeholder' => array()
            );
        }
        $sm = $this->pdo->prepare($data['sql']);
        foreach($data['placeholder'] as $k => $v){
            $sm->bindValue($k, $v);
        }
        $sm->execute();
        return $this->pdo->lastInsertId();
    }

    public function runUpdate($data)
    {
        if(is_string($data)){
            $data = array(
                'sql' => $data,
                'placeholder' => array()
            );
        }
        $sm = $this->pdo->prepare($data['sql']);
        foreach($data['placeholder'] as $k => $v){
            $sm->bindValue($k, $v);
        }
        $sm->execute();
        return $sm->rowCount();
    }

    public function runDelete($data)
    {
        if(is_string($data)){
            $data = array(
                'sql' => $data,
                'placeholder' => array()
            );
        }
        $sm = $this->pdo->prepare($data['sql']);
        foreach($data['placeholder'] as $k => $v){
            $sm->bindValue($k, $v);
        }
        $sm->execute();
        return $sm->rowCount();
    }

    public function insert($table, $data)
    {
        $sql = $this->sql;
        $sql->table($table);
        foreach($data as $k => $v){
            $sql->set($k, $v);
        }
        return $this->runInsert($sql->getInsertSql());
    }

    public function update($table, $data, $id)
    {
        $sql = $this->sql;
        $sql->table($table);
        foreach($data as $k => $v){
            $sql->set($k, $v);
        }
        $sql->where('id', '=', $id);
        return $this->runUpdate($sql->getUpdateSql());
    }

    public function updateByWhere($table, $data, $where)
    {
        $sql = $this->sql;
        $sql->table($table);
        foreach($data as $k => $v){
            $sql->set($k, $v);
        }
        $sql->whereRaw($where);
        return $this->runUpdate($sql->getUpdateSql());
    }

    public function delete($table, $id)
    {
        $sql = $this->sql;
        $sql->table($table);
        $sql->where('id', 'in', $id);
        return $this->runDelete($sql->getDeleteSql());
    }

    public function one($table, $id)
    {
        $sql = $this->sql;
        $sql->table($table);
        $sql->where('id', '=', $id);
        $sql->limit(1, 1);
        return $this->fetch($sql->get());
    }

    public function getOne($table, $field, $value)
    {
        $sql = $this->sql;
        $sql->table($table);
        $sql->where($field, '=', $value);
        $sql->limit(1, 1);
        return $this->fetch($sql->get());
    }

    public function more($table, $id)
    {
        $sql = $this->sql;
        $sql->table($table);
        $sql->where('id', 'in', $id);
        return $this->fetchAll($sql->get());
    }

    public function beginTransaction()
    {
        return $this->pdo->beginTransaction();
    }

    public function commit()
    {
        return $this->pdo->commit();
    }

    public function rollBack()
    {
        return $this->pdo->rollBack();
    }

}
